package org.arl.modem.transportARQ;

import jade.core.AID;
import jade.core.behaviours.WakerBehaviour;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import org.arl.modem.FAPIAgent;
import org.arl.modem.FAPIMessage;
import org.arl.modem.InputPDU;
import org.arl.modem.OutputPDU;
import org.arl.modem.link.Link;
import org.arl.modem.phy.ModemParam;
import org.arl.modem.phy.Physical;
import org.arl.modem.phy.SchemeParam;

@SuppressWarnings("serial")
public class TransportARQ extends FAPIAgent {

   // Params - should be configurable
   public int PROTOCOL = 4; // used for link layer - PROTOCOL
   public int SCHEME_DATA = 2;// used for link layer -SCHEME
   public int SCHEME_CONTROL = 1;// used for link layer -SCHEME

   public int SCHEME_DATA_TRIG = 3;// scheme for data TRIGGER
   public static int FIXED_PROC_TIME = 316; // FIXME Rohit, Apr 5, 2012:
                                            // calculate and add value
   public static int VAR_PROC_TIME = 156; // // FIXME Rohit, Apr 5, 2012:
                                          // calculate and add values
   public static int GUARD_TIME = 20;
   public static int THETA_TIME = 0;
   public int ENABLE = 0;
   public static int ARQ_TYPE = 1; // 0=fixed ARQ 1=JARQ 2=variable ARQ
                                   // 3=rateless JARQ
   public static int RATELESS_CODING_TYPE = 0;// 0=rateless with kentmap 1=LT
   // 2=RLFountain
   public static double BER_CONTROL = 1e-3;// 0.001;// 1e-3;
   public static double BER_DATA = 1e-3;// 0.01;// 1e-2;
   public static double DETECTION_PROB_CONTROL = .99;
   public static double DETECTION_PROB_DATA = .99;
   public final static String NOTIFICATIONS = "tsp-ntf";
   public static int VARTIME_ADD = 10;
   public static int VARTIME_SUB = 10;
   public java.util.logging.Logger mylog;
   // All packet types supported
   public static final int MESSAGETYPE_DATA = 0;
   public final static int MESSAGETYPE_ACK = 1;
   public static final int MESSAGETYPE_REMOTECMD = 2;
   public static final int MESSAGETYPE_REMOTERES = 6;
   private final int MESSAGETYPE_INFO = 3;
   private final int MESSAGETYPE_RXCOMPLETE = 4;
   private final int MESSAGETYPE_INFOACK = 5;

   // subscribe to layers needed

   private AID lnk;
   private AID rng;
   private AID tsp;
   private AID phy;
   private AID cmd;
   private AID notify;

   public static int myOverheadBytes = 3;
   public final static int otherOverheadBytes = 4; // link layer + PHY (3+1)

   // calculation parameters
   public static int blockSize = 14; // number of packets in a block
   public int k = 1; // k=1 means fixed block ARQ
   private stopWatch sw = new stopWatch();
   private int blockSizeCalculated = 14;
   // Variables for TX
   private int txStarted; // 1=tx has started
   private WakerBehaviour wakeupTimer;
   private PacketFactory factory;
   private List<Packet> packetsInTransmission;
   public int jarqDelta = 1500;
   // value
   private Packet currentPacket;
   private FAPIMessage currentMessage;
   private Iterator<Packet> itrPackets;
   private int blockPacketNumber;
   private FAPIMessage reTransmitMsg;
   private boolean rxComplete = false;
   private boolean infoAckRecived = false;
   public TxTimer txTimer;
   private long firstBlockStartTime = -1;
   private long firstBlockEndTime = -1;
   private boolean firstpkt = true;

   // Variables for RX
   private long infoRxTime;
   private long infoAckTxTime;
   public long currentRxTime;
   private long prevRxTime = -1;
   public RXHelper rxHelper; // helper class to handle complete RX
   private int rxBlockStarted = 0; // 1 if block rx starts and 0 once ACK
                                   // sent
   public int rxSrc, rxDest;
   private RxTimer rxTimer; // / adjust after RX not proper!!!!&&&&$$$$$$$$$
                            // FIXXX

   // common for RX,TX
   public Calculator calc;
   private Integer MYADDR = -1;
   // private long starttime = -1;
   private boolean callCalculations = false;
   public int phyProcTime = 100; // FIXME Rohit, Feb 8, 2012: remove val
                                 // &calculate
   public int otherPRocTime = 100;
   private long txPktDuration;
   private int totalPackets;

   public int jarqPktDur;
   private int frameLenData = 0;
   private int frameLenControl = 0;
   private int dataPacketDuration;
   public int jarqPktdurControl;
   private double range;
   private byte[] rxFullData;
   private int testcount = 0;
   private boolean remoteCmdReplyPending = false;
   private int remoteSrc;
   private int remoteDest;
   // private int currDataPktlen;
   // private int currControlPktlen;
   private int loopBackCount = 0;
   // &calculate
   public static double propDelay = .477;// 2.0;// 0.477;// 2.0;// //
                                         // 1.0;//
                                         // 0.65;//
                                         // 1.35;//
                                         // 0.45;//
                                         // 1.35;//
                                         // .003;//
                                         // 1.35; // in
                                         // secs
   // public static long transportProctime = 0;
   public static int MAXPKTDURATION = 700;

   public long blockStartTx = 0;
   public long blockEndTx = 0;
   private int txPacketCounter;
   public ArrayList<TxHelper> txhelper = new ArrayList<TxHelper>();
   private int currentBlockID = -1;
   public int rx_proctime_data;
   public int tx_proctime_data;
   public int tx_proctime_control;
   public int rx_proctime_control;
   private int PACKETLOSS = 0;
   private boolean nopktloss = true;
   public int lastblockSize;
   public int totalBytes;

   public void setup() {
      super.setup();
      subscribeTopic("lnk-ntf");
      subscribeTopic("tsp-ntf");
      subscribeTopic("phy-ntf");
      subscribeTopic("rng-ntf");
      mylog = this.log;
      notify = getTopic(NOTIFICATIONS);
      this.lnk = new AID(getAgentProperty("lnk", "lnk"), AID.ISLOCALNAME);
      this.tsp = new AID(getAgentProperty("tsp", "tsp"), AID.ISLOCALNAME);
      this.phy = new AID(getAgentProperty("phy", "phy"), AID.ISLOCALNAME);
      this.cmd = new AID(getAgentProperty("cmd", "cmd"), AID.ISLOCALNAME);
      this.rng = new AID(getAgentProperty("rng", "rng"), AID.ISLOCALNAME);
      this.txStarted = 0;
      info("$Id: TransportSW agent $");
   }

   /**
    * main entry point for agent for any data or ACK or info messages
    */
   protected void handleMessage(FAPIMessage msg) {

      /**
       * check if the message is from Link layer
       */
      // System.out.println("sender:" + msg.getSender());
      if (msg.getSender().equals(this.cmd) && remoteCmdReplyPending) {
         // if (msg.) {

         if (msg.getData() != null) {
            // System.out.println("convid top:" + msg.getConversationId());
            String resp = new String(msg.getData());
            if (resp.startsWith("~")) {
               System.out.println("response from command: " + resp);

               // send reply

               TransportPacket ackPacket = new TransportPacket();
               ackPacket.messageType = MESSAGETYPE_REMOTERES;
               ackPacket.messageLength = msg.getData().length;
               ackPacket.blockSize = blockSize;
               ackPacket.packetId = -1;
               ackPacket.src = remoteSrc;
               ackPacket.dest = remoteDest;
               ackPacket.blockPacketId = 0;// blockPacketId;
               ackPacket.messageData = msg.getData();
               byte[] buf = ackPacket.encode(msg.getData().length);
               FAPIMessage ackMessage = createMessage(FAPIMessage.REQUEST,
                     this.lnk, buf);
               ackMessage.add("destination", remoteDest);
               ackMessage.add("protocol", PROTOCOL);
               ackMessage.add("scheme", SCHEME_DATA);
               send(ackMessage);
               remoteCmdReplyPending = false;
            }
         }
      }
      if (msg.getSender().equals(this.rng)) {
         // System.out.println("rng:");
         switch (msg.getPerformative()) {
         case FAPIMessage.INFORM:
            if (ENABLE == 0)
               break;
            // Range notification
            if (msg.get("type").equals("RANGE")) {
               range = msg.getAsDouble("range", 12);
               System.out.println("range is:" + range);
               propDelay = range / 1540;
               System.out.println("propdelay calculated:" + propDelay);
               log.fine("propdelay calculated:" + propDelay);
               // initARQType();
               // calculationsInit();
            }
            break;
         }
      }
      if (msg.getSender().equals(this.phy)) {
         // if (msg.get("type").equals("RX.PKT.TIME")) {
         // loopBackCount++;
         // if (loopBackCount == 1)
         // preLoopBack2();
         // if (loopBackCount == 2)
         // postLoopBack();
         // }
         switch (msg.getPerformative()) {
         case FAPIMessage.INFORM:
            if (ENABLE == 0) {
               // if (msg.get("type").equals("TX.PKT.TIME")) {
               //
               // } else if (msg.get("type").equals("RX.PKT.TIME")) {
               // long sendtime = msg.getAsLong("time", -1);
               // // long st = System.currentTimeMillis();
               // // long now = getPhyTime();
               // // long durrr = System.currentTimeMillis() - st;
               // sendtime = sendtime + 40 * 1000 + VARTIME_ADD * 1000;
               // // log.fine("POWER Now time is:" + now);
               // // long dur = now-msg.getAsLong("time", -1);
               // // log.fine("POWER: notification came after=" + dur);
               // // log.fine("POWER: phy query took=" + durrr);
               // log.fine("TESTING POWER AMPLIFIER TIME=" + sendtime);
               // FAPIMessage msg11 = createMessage(FAPIMessage.REQUEST, phy,
               // Physical.TRIGGER);
               // msg11.add("time", sendtime);
               // send(msg11);
               // }
               break;
            }
            // PHY layer TX notification
            if (msg.get("type").equals("TX.PKT.TIME")) {
               log.fine("$$$PTX:" + System.currentTimeMillis());
               long temp12 = System.currentTimeMillis() - txPktDuration;
               log.fine("tsp:gap between 2 tx(phy) (msec):" + temp12);
               txPktDuration = System.currentTimeMillis();
               long phyTxTime = msg.getAsLong("time", -1) / 1000;
               log.fine("TX timestamp####" + phyTxTime);
               if (txTimer == null) {
                  txTimer = new TxTimer(phyTxTime, this);
                  // /txTimer = new TxTimer(System.currentTimeMillis(), this);
               }

               if (infoAckRecived && txTimer.getFirstPktTxtime() == -1) {
                  txTimer.init(phyTxTime);
                  firstBlockStartTime = phyTxTime;
                  // / txTimer.init(System.currentTimeMillis());
               }

               if (rxTimer != null && rxTimer.getInfoACKTxTime() == -1) {
                  infoAckTxTime = msg.getAsLong("time", -1);
                  rxTimer.setInfoACKTxTime(infoAckTxTime);
               }
               if (infoAckRecived)
                  txPacketCounter++;
               // System.out.println("block pkt no:" + blockPacketNumber + ":"
               // + txPacketCounter);
               // if (blockPacketNumber == blockSize) {
               if (txPacketCounter % blockSize == 0 && infoAckRecived) {
                  // lastblockSize = blockSize;
                  if (firstBlockEndTime == -1) {
                     // firstBlockEndTime = System.currentTimeMillis();
                     firstBlockEndTime = phyTxTime;
                     txTimer.setBlockTxTime(firstBlockEndTime
                           - firstBlockStartTime + jarqPktDur);
                     log.fine("tsp:block Tx time taken:"
                           + txTimer.getBlockTxTime());
                  }
                  if (wakeupTimer != null)
                     removeBehaviour(wakeupTimer);

                  // System.out.println("currentblockid=" + currentBlockID
                  // + "  tx timestamp=" + phyTxTime);
                  long expected = (txTimer.getTwoWayDelay() - jarqPktdurControl + jarqPktDur);
                  txhelper
                        .add(new TxHelper(currentBlockID, phyTxTime, expected));

                  int wakuptime = (int) (txTimer.getDelta() / 1000);
                  // wakuptime = wakuptime - phyProcTime - otherPRocTime;
                  // wakuptime = wakuptime + 100;
                  log.fine("tsp: wakeup time for next TX is(ms) :" + wakuptime);
                  wakeupTimer = new WakerBehaviour(this, wakuptime) {
                     @Override
                     protected void handleElapsedTimeout() {
                        firstpkt = true;
                        if (!rxComplete) {
                           System.out
                                 .println("timer expires$$$$$$$$$$$$$$$$$$$$$$$$$$");
                           log.fine("$$$$TIMER EXPIRES$$$$$");
                           // sendNextBlock(reTransmitMsg);
                        }
                     }
                  };

                  if (ARQ_TYPE == 1 || ARQ_TYPE == 3) {
                     firstpkt = true;
                     if (!rxComplete) {
                        // System.out.println("block pkt no:" +
                        // blockPacketNumber
                        // + ": start the next block");
                        sendNextBlock(reTransmitMsg);
                        log.fine("send next block call finished");
                     }
                     long timeToTrigger = phyTxTime * 1000 + txTimer.getDelta();
                     log.fine("timeToTrigger=" + timeToTrigger);

                     FAPIMessage msg11 = createMessage(FAPIMessage.REQUEST,
                           phy, Physical.TRIGGER);
                     msg11.add("time", timeToTrigger);
                     send(msg11);
                  } else {
                     firstpkt = true;
                     // addBehaviour(wakeupTimer);
                  }

               } else if (blockPacketNumber < blockSize
                     && blockPacketNumber != 0) {
                  sendNextPacket(SCHEME_DATA);
               }

            }// PHY layer RX notification
            else if (msg.get("type").equals("RX.PKT.TIME")) {
               log.fine("$$$PRX:" + System.currentTimeMillis());
               currentRxTime = msg.getAsLong("time", -1);
               log.fine("RX timestamp####" + currentRxTime);
               infoRxTime = currentRxTime;
               // FIXME Rohit, Feb 23, 2012:******* removed for testing
               // if (txTimer != null && txTimer.getInfoACKRxtime() == -1) {
               // txTimer.setInfoACKRxtime(currentRxTime);
               // }
               if (prevRxTime == -1) {
                  prevRxTime = currentRxTime;
               }
               long gap = currentRxTime - prevRxTime;
               log.fine("tsp:Gap between 2 Rx(phy) (ms):" + gap);
               prevRxTime = currentRxTime;
            }
            break;
         }
      }

      // IF sent by link layer
      if (msg != null && msg.getSender() != null
            && msg.getSender().equals(this.lnk)) {
         if (msg.getAsInteger("protocol") != null
               && msg.getAsInteger("protocol") == this.PROTOCOL) {
            switch (msg.getPerformative()) {
            case FAPIMessage.INFORM:
               if (ENABLE == 1) {

                  log.fine("$$$TRX start:" + System.currentTimeMillis());
                  Random r = new Random();
                  int err = r.nextInt(25);
                  // if (testcount < 7 || testcount > 8) {
                  // System.out.println("random number:" + err);
                  if (PACKETLOSS == 1) {
                     // if ((testcount != 3 && testcount != 5) || nopktloss) {
                     if (err != 3 || nopktloss) {
                        handleRxFAPI(msg);
                     } else {
                        System.out
                              .println("missed packet$$$$$$$$$$$$$$$$$$$$$");

                     }
                  } else {
                     handleRxFAPI(msg);
                  }
                  testcount++;
               }
               break;
            }
         }
      }

      /**
       * check if its sent from Transport layer
       */
      if (msg.getAsInteger("STOP") != null && msg.getAsInteger("STOP") == 1) {
         stopAllTimers();
      }
      if (msg.getAsInteger("RemoteExecution") != null
            && msg.getAsInteger("RemoteExecution") == 1) {
         // FIXME Rohit, Jan 12, 2012: test the remote execution
         handleRemoteExec(msg);
      }
      // }
      if (msg.containsData()) {// do tx
         if (msg.getAsInteger("tx") != null && msg.getAsInteger("tx") == 1) {
            resetTx();
            if (ENABLE == 1) {
               if (msg.getAsInteger("file") != null
                     && msg.getAsInteger("file") == 1) {
                  convertFileToBytes(msg);

                  // } else {
                  // if (ARQ_TYPE == 3) {
                  // log.fine("tsp:ERROR!!! Rate less ARQ does not support ttx.pkt");
                  // return;
                  // }
               }
               System.out.println("total bytes in file = "
                     + msg.getData().length);
               totalBytes = msg.getData().length;
               txPktDuration = System.currentTimeMillis();
               log.fine("$$$TTX start:" + System.currentTimeMillis());
               handleTxFAPI(msg);
            }
         }
      } else {// set params
         handleRequest(msg);
      }

   }

   private void convertFileToBytes(FAPIMessage msg) {
      String s = new String(msg.getData());
      String filepath = convertHexToString(s);
      File file = new File(filepath.toLowerCase());

      byte[] b = new byte[(int) file.length()];
      try {
         FileInputStream fileInputStream = new FileInputStream(file);
         fileInputStream.read(b);
         // for (int i = 0; i < b.length; i++) {
         // System.out.print((char) b[i]);
         // }
      } catch (FileNotFoundException e) {
         log.fine("File Not Found.");
         e.printStackTrace();
      } catch (IOException e1) {
         log.fine("Error Reading The File.");
         e1.printStackTrace();
      }
      msg.setData(b);

   }

   private void handleRequest(FAPIMessage msg) {
      String action = msg.getContent();
      if (action == null) {
         action = "";
      }
      FAPIMessage rsp = msg.createReply();
      rsp.setPerformative(FAPIMessage.NOT_UNDERSTOOD);
      if (action.equals(PARAM_SET)) { // param set request
         int ppp = msg.getAsInteger("parameter");
         int vvv = msg.getAsInteger("value");
         boolean err = false;
         switch (ppp) {
         case TransportParam.PROTOCOL:
            PROTOCOL = vvv;
            break;
         case TransportParam.SCHEME_DATA:
            SCHEME_DATA = vvv;
            break;

         case TransportParam.ENABLE:
            ENABLE = vvv;
            if (ENABLE == 1)
               callCalculations = true;
            break;
         case TransportParam.ARQ_TYPE:
            ARQ_TYPE = vvv;
            if (ENABLE == 1)
               callCalculations = true;

            break;
         case TransportParam.FIXED_PROC_TIME:
            FIXED_PROC_TIME = vvv;
            break;
         case TransportParam.VAR_PROC_TIME:
            VAR_PROC_TIME = vvv;
            break;
         case TransportParam.GUARD_TIME:
            GUARD_TIME = vvv;
            break;
         case TransportParam.SCHEME_DATA_TRIG:
            SCHEME_DATA_TRIG = vvv;
            break;

         case TransportParam.SCHEME_CONTROL:
            SCHEME_CONTROL = vvv;
            break;
         case TransportParam.VARTIME_ADD:
            VARTIME_ADD = vvv;
            // callCalculations = true;
            break;
         case TransportParam.VARTIME_SUB:
            VARTIME_SUB = vvv;
            callCalculations = true;
            break;
         case TransportParam.PROP_DELAY:
            propDelay = (double) vvv / 1000.0;
            break;
         case TransportParam.PHYPROCTIME:
            phyProcTime = vvv;
            break;
         case TransportParam.PACKETLOSS:
            PACKETLOSS = vvv;
            break;
         case TransportParam.OTHERPROCTIME:
            otherPRocTime = vvv;
            break;
         case TransportParam.MAXPKTDURATION:
            MAXPKTDURATION = vvv;
            break;
         case TransportParam.RATELESS_CODING_TYPE:
            RATELESS_CODING_TYPE = vvv;
            break;
         default:
            err = true;
         }
         if (err)
            rsp.setPerformative(FAPIMessage.FAILURE);
         else {
            rsp.setPerformative(FAPIMessage.INFORM);
            rsp.setContent(PARAM_SET);
            rsp.add("parameter", ppp);
            rsp.add("value", vvv);
         }
      } else if (action.equals(PARAM_GET)) { // param get request
         int ppp = msg.getAsInteger("parameter");
         long vvv = -1;
         switch (ppp) {
         case TransportParam.PROTOCOL:
            vvv = PROTOCOL;
            break;
         case TransportParam.SCHEME_DATA:
            vvv = SCHEME_DATA;
            break;
         case TransportParam.ENABLE:
            vvv = ENABLE;
            break;
         case TransportParam.ARQ_TYPE:
            vvv = ARQ_TYPE;
            break;
         case TransportParam.SCHEME_CONTROL:
            vvv = SCHEME_CONTROL;
            break;
         case TransportParam.VARTIME_ADD:
            vvv = VARTIME_ADD;
            break;
         case TransportParam.VARTIME_SUB:
            vvv = VARTIME_SUB;
            break;
         case TransportParam.FIXED_PROC_TIME:
            vvv = FIXED_PROC_TIME;
            break;
         case TransportParam.VAR_PROC_TIME:
            vvv = VAR_PROC_TIME;
            break;
         case TransportParam.GUARD_TIME:
            vvv = GUARD_TIME;
            break;
         case TransportParam.SCHEME_DATA_TRIG:
            vvv = SCHEME_DATA_TRIG;
            break;
         case TransportParam.OTHERPROCTIME:
            vvv = otherPRocTime;
            break;
         case TransportParam.PHYPROCTIME:
            vvv = phyProcTime;
            break;
         case TransportParam.PROP_DELAY:
            vvv = (int) (propDelay * 1000.0);
            break;

         case TransportParam.RATELESS_CODING_TYPE:
            vvv = RATELESS_CODING_TYPE;
            break;
         case TransportParam.PACKETLOSS:
            vvv = PACKETLOSS;
            break;
         case TransportParam.MAXPKTDURATION:
            vvv = MAXPKTDURATION;
            break;
         }
         if (vvv < 0) {
            rsp.setPerformative(FAPIMessage.FAILURE);
         }

         else {
            rsp.setPerformative(FAPIMessage.INFORM);
            rsp.setContent(PARAM_GET);
            rsp.add("parameter", ppp);
            rsp.add("value", vvv);
         }
      }
      send(rsp);
      if (callCalculations) {
         callCalculations = false;
         initARQType();
         preLoopBack();
      }

   }

   private void initARQType() {
      myOverheadBytes = 3;
      switch (ARQ_TYPE) {
      case 0:// fixed block ARQ

         break;
      case 1: // JARQ

         break;
      case 2: // variable block ARQ
         myOverheadBytes = myOverheadBytes + 1;
         break;
      case 3:// rateless JARQ
         myOverheadBytes = myOverheadBytes + 1;
         break;
      default:
         break;
      }
   }

   /**
    * Handles the received FAPI message (incoming)
    * @param msg
    */
   private void handleRxFAPI(FAPIMessage msg) {
      long timm = System.nanoTime();
      final TransportPacket tp = new TransportPacket();
      tp.decode(msg.getData());
      // if (tp.blockPacketId != 2) {
      FAPIMessage ntf = createMessage(FAPIMessage.INFORM, notify);
      tp.src = Integer.parseInt(msg.get("source"));
      tp.dest = Integer.parseInt(msg.get("destination"));
      ntf.add("src", tp.src);
      ntf.add("dest", tp.dest);
      ntf.add("RX", 1);
      ntf.setData(tp.messageData);

      switch (tp.messageType) {
      case MESSAGETYPE_DATA: // Data Packet received
         /**
          * if data packet received create and send the ACK
          */
         log.fine("tsp:Packet ID recv:" + tp.packetId);
         ntf.add("TYPE", "RX.DATA");
         if (rxBlockStarted == 0) {
            rxBlockStarted = 1;
         }

         int rxtreesize = rxHelper.addRXPacket(tp);
         log.fine("RX TREE size=" + rxtreesize);
         log.fine("tsp:RX pkt ID:" + tp.blockPacketId + ":" + tp.packetId);
         rxTimer.ackPacketatRX(currentRxTime, tp);
         break;

      case MESSAGETYPE_RXCOMPLETE: // RX complete received
         rxComplete = true;
         sw.stop();
         stopAllTimers();
         // no break statement as ACK is necessar for last one
      case MESSAGETYPE_ACK: // ACK packet received
         ntf.add("TYPE", "RX.ACK");
         if (ARQ_TYPE != 3) {
            // System.out.println("ACK data length:" + tp.messageData.length);
            // int ackFrameLen = blockSize / 8;
            // if (blockSize % 8 != 0)
            // ackFrameLen++;

            int ackFrameLen = 0;
            if (ARQ_TYPE == 2) {
               ackFrameLen = lastblockSize / 8;
               if (lastblockSize % 8 != 0)
                  ackFrameLen++;
            } else {
               ackFrameLen = blockSize / 8;
               if (blockSize % 8 != 0)
                  ackFrameLen++;
            }

            byte ackBuf[] = new byte[ackFrameLen];
            // System.out.println("tp.msgdata.length=" + tp.messageData.length);
            // System.out.println("tp.ackframelength=" + ackFrameLen);
            for (int x = 0; x < ackFrameLen; x++) {
               ackBuf[x] = tp.messageData[x];
            }
            boolean bo[] = TransportPacket.byteToBool(ackBuf);
            // log.fine("booooo size:lastblocksize=" + bo.length + ":"
            // + lastblockSize);
            factory.ackPacket(factory.getBlockIdtobeAcked(k), bo);
            // factory.ackPacket(factory.getBlockIdtobeAcked(k), ackBuf);
            ;
            factory.cleanupBlock(factory.getBlockIdtobeAcked(k));

            if (!rxComplete) {
               // create FAPIMESSAGE to send DATA via Transport layer
               FAPIMessage msgNext = createMessage(FAPIMessage.REQUEST, tsp);
               msgNext.add("src", tp.dest);
               msgNext.add("dest", tp.src);
               msgNext.add("tx", 1);
               switch (ARQ_TYPE) {
               case 0:// fixed ARQ
                  if (wakeupTimer != null) {
                     wakeupTimer.stop();
                     removeBehaviour(wakeupTimer);
                  }
                  sendNextBlock(reTransmitMsg);
                  break;
               case 1:// fixed JARQ
                  // do nothing
                  break;
               case 2:// variable ARQ
                  if (wakeupTimer != null) {
                     wakeupTimer.stop();
                     removeBehaviour(wakeupTimer);
                  }
                  sendNextBlock(reTransmitMsg);
                  break;
               case 3:// rateless JARQ

                  break;
               default:
                  break;
               }
            } else {
               // TX is completed
               System.out.println("tx completed!!!");
               if (wakeupTimer != null) {
                  wakeupTimer.stop();
                  removeBehaviour(wakeupTimer);
               }
               FAPIMessage ntf2 = createMessage(FAPIMessage.INFORM, notify);
               ntf2.add("src", tp.src);
               ntf2.add("dest", tp.dest);
               ntf2.add("done", 1);
               ntf2.add("TYPE", "TX.COMPLETE");
               ntf2.add(
                     "msg",
                     "Transmission completed with ACK in: "
                           + sw.getElapsedTime() + "(msec)");
               send(ntf2);
               resetTx();
            }
         } else {
            if (rxComplete) {
               FAPIMessage ntf2 = createMessage(FAPIMessage.INFORM, notify);
               ntf2.add("src", tp.src);
               ntf2.add("dest", tp.dest);
               ntf2.add("done", 1);
               ntf2.add("TYPE", "TX.COMPLETE");
               ntf2.add(
                     "msg",
                     "Transmission completed with ACK in: "
                           + sw.getElapsedTime() + "(msec)");
               send(ntf2);
               resetTx();
            }
         }

         break;

      case MESSAGETYPE_REMOTECMD: // Remote execution request
         ntf.add("TYPE", "RX.DATA");
         String s = new String(tp.messageData);
         FAPIMessage req = createMessage(FAPIMessage.REQUEST, cmd,
               convertHexToString(s, tp.messageLength));

         FAPIMessage rep = request(req, 2000);
         System.out.println("convid:" + rep.getConversationId());
         remoteCmdReplyPending = true;
         remoteSrc = tp.dest;
         remoteDest = tp.src;
         if (rep == null || rep.getPerformative() != FAPIMessage.INFORM) {

            System.out.println("dfgdf  error");
            // System.out.println("reply:" + new String(rep.getData()));
            return;
         }
         System.out.println("reply:" + new String(rep.getData()));
         System.out.println("OK");
         send(req);

         break;

      case MESSAGETYPE_REMOTERES: // Remote execution request
         ntf.add("TYPE", "RX.DATA");
         break;
      case MESSAGETYPE_INFO: // Info packet received (how many packets in
                             // current transmission)

         ntf.add("TYPE", "RX.INFO");
         InputPDU in = new InputPDU(tp.messageData);
         totalPackets = in.read32();
         totalBytes = in.read16();
         int totalbytes = 0;
         switch (ARQ_TYPE) {
         case 0:// Fixed ARQ
            if (blockSize > totalPackets)
               blockSize = totalPackets;
            break;
         case 1:// Fixed JARQ

            break;
         case 2:// variable ARQ

            break;
         case 3:// rateless JARQ
            totalbytes = totalPackets;
            break;
         }
         int pktDuration = dataPacketDuration;
         rxTimer = new RxTimer(infoRxTime, this, pktDuration);
         rxHelper = new RXHelper(totalPackets,
               getFrameLength(MESSAGETYPE_DATA), totalbytes);
         sendStartInfoACK(tp);
         nopktloss = false;
         break;
      case MESSAGETYPE_INFOACK:
         if (txTimer != null && txTimer.getInfoACKRxtime() == -1) {
            // / txTimer.setInfoACKRxtime(System.currentTimeMillis());
            txTimer.setInfoACKRxtime(msg.getAsLong("time", -1) / 1000);
         }
         ntf.add("TYPE", "RX.INFOACK");
         FAPIMessage msgNext = this.createMessage(FAPIMessage.REQUEST, tsp);
         msgNext.add("src", tp.dest);
         msgNext.add("dest", tp.src);
         msgNext.add("tx", 1);
         infoAckRecived = true;
         // System.out.println("info ACK has come");
         handleTxFAPI(msgNext);
         nopktloss = false;
         break;
      default:

         log.warning("Error! Message Type does not exist");
         return;
         // break;
      }
      send(ntf);
      // }
      // log.fine("TRX.pkt -end:" + getPhyTime());
      timm = System.nanoTime() - timm;
      log.fine("$$$TRX end:" + System.currentTimeMillis());
      log.fine("tsp:Transport RX Processing time:" + timm);
   }

   private void resetTx() {
      txStarted = 0;
      stopAllTimers();
      infoAckRecived = false;
      rxComplete = false;
      if (packetsInTransmission != null) {
         packetsInTransmission.clear();
      }
      txTimer = null;
      firstBlockEndTime = -1;
      firstBlockStartTime = -1;
      firstpkt = true;
      frameLenControl = 0;
      frameLenData = 0;
      blockPacketNumber = 0;
      txhelper.clear();
   }

   private void resetRx() {
      txhelper.clear();
      frameLenControl = 0;
      frameLenData = 0;
   }

   private void handleTxFAPI(FAPIMessage msg) {
      long timm = System.nanoTime();
      // System.out
      // .println("txstarted:infoack" + txStarted + ":" + infoAckRecived);
      if (this.txStarted == 0) {
         // start transmission session and reset the counters
         this.txStarted = 1;
         byte[] buf = new byte[msg.getData().length];
         buf = msg.getData();
         int totalpackets = 0;
         int flen = getFrameLength(MESSAGETYPE_DATA);
         // System.out.println("msg length in handletxfapi:" + buf.length + ":"
         // + flen);
         if (ARQ_TYPE == 2)
            blockSize = blockSizeCalculated;
         factory = new PacketFactory(flen, blockSize, this);
         // System.out.println();
         totalpackets = factory.createPackets(buf.clone());
         sw.start();
         rxComplete = false;
         // send the start transmission packet
         msg.setData(null);
         OutputPDU o = new OutputPDU(4);

         switch (ARQ_TYPE) {
         case 0:// fixed ARQ

            if (blockSize > totalpackets) {
               blockSize = totalpackets;
               factory.setBlockSize(blockSize);
            }
            o.write32(totalpackets);
            break;
         case 1:// JARQ
            o.write32(totalpackets);
            break;
         case 2:// variable ARQ
            o.write32(totalpackets);
            break;
         case 3:// rateless JARQ
            // System.out.println("msg length:" + msg.getData().length);
            o.write32(totalpackets);
            break;
         }
         o.write16(totalBytes);
         msg.setData(o.toByteArray());
         infoAckRecived = false;
         sendStartInfo(msg);
         // calculationsInit();
      }
      if (this.txStarted == 1 && infoAckRecived) {
         msg.setData(null);
         reTransmitMsg = msg;
         // delegate the transmission to sendNextBlock
         firstBlockStartTime = -1;
         // sendNextBlock(msg);
         sendNextBlockNow(msg);
         txPacketCounter = 0;
      }
      timm = System.nanoTime() - timm;
      log.fine("tsp:Transport TX processing time:" + timm);

   }

   public void sendAckMessage(int blockPacketId, int src, int dest, byte[] data) {

      rxBlockStarted = 0;

      // Create Transport layer ACK packet
      TransportPacket ackPacket = new TransportPacket();
      if (rxHelper.checkIFComplete()) {
         ackPacket.messageType = MESSAGETYPE_RXCOMPLETE;

      } else {
         ackPacket.messageType = MESSAGETYPE_ACK;
         // log.fine("RX Data:" + new String(rxHelper.getRxDataAsBytes()));
      }
      ackPacket.messageLength = data.length;
      ackPacket.blockSize = blockSize;
      ackPacket.packetId = -1;
      ackPacket.src = src;
      ackPacket.dest = dest;
      ackPacket.blockPacketId = blockPacketId;
      ackPacket.messageData = data;
      for (final byte b : data) {
         log.fine(Integer.toString(b & 0xFF, 2));
         log.fine(":");

      }
      log.fine("");
      // byte[] buf = ackPacket.encode(getFrameLength(ackPacket.messageType));
      byte[] buf = ackPacket.encode(data.length);
      // log.fine("ACK msg buffer length:" + buf.length);
      // create a FAPIMESSAGE to send the ACK via LINK layer
      FAPIMessage ackMessage = createMessage(FAPIMessage.REQUEST, this.lnk, buf);
      ackMessage.add("destination", dest);
      ackMessage.add("protocol", PROTOCOL);
      ackMessage.add("scheme", SCHEME_CONTROL);

      send(ackMessage);

      if (rxHelper.checkIFComplete()) {
         byte allData[] = rxHelper.getRxDataAsBytes();
         rxFullData = new byte[totalBytes];
         System.arraycopy(allData, 0, rxFullData, 0, totalBytes);
         // rxFullData =

         FileOutputStream out = null;
         try {
            out = new FileOutputStream("/home/rohit/Desktop/testSimRX.dat");
            for (byte bb : rxFullData) {
               out.write(bb);
            }
            // out.write(rxFullData);
            out.close();
         } catch (Exception e) {
            e.printStackTrace();
         }

         FAPIMessage ntf2 = createMessage(FAPIMessage.INFORM, notify);
         ntf2.add("src", src);
         ntf2.add("dest", dest);
         ntf2.add("done", 1);
         ntf2.add("TYPE", "TX.COMPLETE");
         ntf2.add("msg", "Transmission completed with ACK");
         ntf2.setData(rxFullData);
         send(ntf2);
         resetTx();

         log.fine("RX Data:" + new String(rxFullData));
      }

   }

   private void sendStartInfo(FAPIMessage msg) {
      TransportPacket tp = new TransportPacket();
      tp.messageType = MESSAGETYPE_INFO; // info packet
      tp.messageLength = msg.getData().length; // msg.getAsInteger("len");
      tp.blockSize = 1;
      tp.blockPacketId = 0;
      tp.messageData = msg.getData();
      tp.src = msg.getAsInteger("src");
      // MYADDR = msg.getAsInteger("src");
      tp.dest = msg.getAsInteger("dest");
      tp.packetId = -1;
      tp.seed = 0;
      tp.degree = 0;
      int pklen = getFrameLength(tp.messageType);
      // System.out.println("pkt length in send start:" + pklen);
      byte[] buf = tp.encode(pklen);
      // byte[] buf = tp.encode(getFrameLength(tp.messageType));
      FAPIMessage req = createMessage(FAPIMessage.REQUEST, this.lnk);
      req.add("destination", tp.dest);
      req.add("protocol", PROTOCOL);
      req.add("scheme", SCHEME_DATA);
      req.setData(buf);
      send(req);

      FAPIMessage rsp = msg.createReply();
      rsp.setPerformative(FAPIMessage.AGREE);
      send(rsp);
      log.fine("$$$TTX end :" + System.currentTimeMillis());

   }

   private void sendStartInfoACK(TransportPacket tp2) {
      TransportPacket tp = new TransportPacket();
      tp.messageType = MESSAGETYPE_INFOACK; // info packet
      tp.messageLength = tp2.messageData.length; // msg.getAsInteger("len");
      tp.blockSize = 1;
      tp.blockPacketId = 0;
      tp.messageData = new byte[1];// tp2.messageData;
      tp.dest = tp2.src;
      tp.src = tp2.dest;
      tp.packetId = -1;
      tp.seed = 0;
      tp.degree = 0;
      byte[] buf = tp.encode(getFrameLength(tp.messageType));
      log.fine("buffer length in info ACK:" + buf.length);
      rxSrc = tp.src;
      rxDest = tp.dest;

      FAPIMessage req = createMessage(FAPIMessage.REQUEST, this.lnk);
      req.add("destination", tp.dest);
      log.fine("destination of infoACK:" + tp.dest);
      req.add("protocol", PROTOCOL);
      req.add("scheme", SCHEME_CONTROL);
      req.setData(buf);
      send(req);
      log.fine("$$$TTX end :" + System.currentTimeMillis());
   }

   /**
    * Send Next Block of packets
    */
   private void sendNextBlock(FAPIMessage msg) {

      if (ARQ_TYPE == 0 || ARQ_TYPE == 2)
         firstpkt = true;
      if (packetsInTransmission != null) {
         packetsInTransmission.clear();
      }
      // System.out.println("getting new block (factory)");
      Block b = this.factory.getNextBlock();
      System.out.println("block size actual=" + b.getPackets().size());
      log.fine("block size actual=" + b.getPackets().size());
      currentBlockID = b.getBlockID();
      packetsInTransmission = b.getPackets();
      msg.add("blockid", b.getBlockID());
      blockPacketNumber = 0;
      itrPackets = packetsInTransmission.iterator();
      // System.out.println("getting new block (factory)END:");
      // send the 1st packet and block the block send
      currentMessage = msg;
      log.fine("$$$TTX start :" + System.currentTimeMillis());
      int scheme = SCHEME_DATA_TRIG;
      if (TransportARQ.ARQ_TYPE == 0 || ARQ_TYPE == 2) {
         scheme = SCHEME_DATA;
      }
      sendNextPacket(scheme);
      if (firstpkt && blockSize > 1) {
         log.fine("$$$TTX start :" + System.currentTimeMillis());
         sendNextPacket(scheme);
         // sendNextPacket();
         firstpkt = false;
      }

   }

   private void sendNextBlockNow(FAPIMessage msg) {

      if (ARQ_TYPE == 0 || ARQ_TYPE == 2)
         firstpkt = true;
      if (packetsInTransmission != null) {
         packetsInTransmission.clear();
      }
      // System.out.println("getting new block (factory)");
      Block b = this.factory.getNextBlock();
      currentBlockID = b.getBlockID();
      packetsInTransmission = b.getPackets();
      msg.add("blockid", b.getBlockID());
      blockPacketNumber = 0;
      itrPackets = packetsInTransmission.iterator();
      // System.out.println("getting new block (factory)END:");
      // send the 1st packet and block the block send
      currentMessage = msg;
      log.fine("$$$TTX start :" + System.currentTimeMillis());
      sendNextPacket(SCHEME_DATA);
      if (firstpkt && blockSize > 1) {
         log.fine("$$$TTX start :" + System.currentTimeMillis());
         sendNextPacket(SCHEME_DATA);
         // sendNextPacket();
         firstpkt = false;
      }

   }

   private void sendNextPacket(int scheme) {
      if (itrPackets.hasNext()) {
         blockPacketNumber++;

         currentPacket = itrPackets.next();
         currentMessage.setData(currentPacket.getData().clone());

         // create a reply for command handler
         FAPIMessage rsp = currentMessage.createReply();
         rsp.setPerformative(FAPIMessage.FAILURE);
         if (doTx(currentMessage, scheme)) { // if the transmission is
                                             // successful
            rsp.setPerformative(FAPIMessage.AGREE);
         }
         send(rsp);
      } else {
         log.fine("ERROR: no packet left in sendNextPacket");
      }

   }

   /**
    * perform actual transmission
    * @param msg
    * @return
    */
   private boolean doTx(final FAPIMessage msg, final int scheme) {
      // create the Transport packet
      TransportPacket tp = new TransportPacket();
      tp.messageType = MESSAGETYPE_DATA;
      tp.messageLength = msg.getData().length; // msg.getAsInteger("len");
      tp.blockSize = blockSize;
      tp.blockPacketId = currentPacket.getBlockPacketId();
      tp.messageData = msg.getData().clone();
      // System.out.println();
      // for (byte b : tp.messageData) {
      // System.out.print(Integer.toString(b, 16).toUpperCase());
      // }

      // System.out.println();
      tp.src = msg.getAsInteger("src");
      tp.dest = msg.getAsInteger("dest");
      tp.packetId = currentPacket.getPacketId();

      tp.seed = currentPacket.getSeed();
      tp.degree = currentPacket.getDegree();
      // encode the transport packet
      byte[] buf = tp.encode(getFrameLength(tp.messageType));

      FAPIMessage req = createMessage(FAPIMessage.REQUEST, this.lnk, buf);
      req.add("destination", tp.dest);
      req.add("protocol", PROTOCOL);
      req.add("scheme", scheme);
      // System.out.println("link layer packet with destination=" + tp.dest
      // + ",scheme=" + scheme + ",protocol=" + PROTOCOL);
      // log.fine("link layer packet with destination=" + tp.dest + ",scheme="
      // + scheme + ",protocol=" + PROTOCOL);
      send(req);
      log.fine("$$$TTX end :" + System.currentTimeMillis());
      return true;
   }

   private void handleRemoteExec(FAPIMessage msg) {
      TransportPacket tp = new TransportPacket();
      tp.messageType = 2;
      tp.messageLength = msg.getData().length;
      System.out.println("send length:" + tp.messageLength);
      tp.blockSize = 1;
      tp.blockPacketId = 0;
      tp.messageData = msg.getData();
      tp.src = msg.getAsInteger("src");
      tp.dest = msg.getAsInteger("dest");

      tp.packetId = -1;
      // encode the transport packet
      byte[] buf = tp.encode(tp.messageLength);

      // byte[] buf = tp.encode(getFrameLength(tp.messageType));
      // log.fine("encoded Transportlayer packet:" + new String(buf));
      FAPIMessage req = createMessage(FAPIMessage.REQUEST, this.lnk);
      req.add("destination", tp.dest);
      req.add("protocol", PROTOCOL);
      req.add("scheme", SCHEME_DATA);
      req.setData(buf);
      send(req);
   }

   public int getPhyParam(int nnn, int mmm) {
      FAPIMessage req = createMessage(FAPIMessage.REQUEST, phy,
            Physical.PARAM_GET);
      req.add("family", nnn);
      req.add("parameter", mmm);
      FAPIMessage reply = request(req, 2000);
      if (reply == null || reply.getPerformative() != FAPIMessage.INFORM)
         return -1;
      return reply.getAsInteger("value");
   }

   public long getPhyTime() {
      FAPIMessage req = createMessage(FAPIMessage.REQUEST, phy,
            Physical.CLOCK_GET);
      FAPIMessage reply = request(req, 2000);
      if (reply == null || reply.getPerformative() != FAPIMessage.INFORM)
         return -1;
      return reply.getAsLong("time", 0);
   }

   public boolean setPhyParam(int nnn, int mmm, int v) {
      // if (v % 3 != 0)
      // log.fine("ERROR TRANSPORT length setting");
      FAPIMessage req = createMessage(FAPIMessage.REQUEST, phy,
            Physical.PARAM_SET);
      req.add("family", nnn);
      req.add("parameter", mmm);
      req.add("value", v);
      // log.fine("SETTING PKTLEN:" + v);
      FAPIMessage reply = request(req, 2000);
      if (reply == null || reply.getPerformative() != FAPIMessage.INFORM) {
         log.fine("error setting pkt len:" + v);
         return false;
      }
      log.fine("length set to:" + v);
      return true;
   }

   private int getLinkParam(int mmm) {
      FAPIMessage req = createMessage(FAPIMessage.REQUEST, lnk, Link.PARAM_GET);
      req.add("parameter", mmm);
      FAPIMessage reply = request(req, 2000);
      if (reply == null || reply.getPerformative() != FAPIMessage.INFORM)
         return -1;
      return reply.getAsInteger("value");
   }

   private void stopAllTimers() {
      if (wakeupTimer != null) {
         wakeupTimer.stop();
         removeBehaviour(wakeupTimer);
      }
   }

   /**
    * Returns the packet length available for data in transport layer after
    * removing the overheads
    * @param messageType : type of message
    * @return
    */
   public int getFrameLength(int messageType) {
      int frameLen = 0;

      if (messageType == MESSAGETYPE_DATA || messageType == MESSAGETYPE_INFO) {
         if (frameLenData == 0) {
            frameLen = getPhyParam(SCHEME_DATA, SchemeParam.PKTLEN);
            frameLenData = frameLen;
         } else {
            frameLen = frameLenData;
         }
      } else {
         if (frameLenControl == 0) {
            frameLen = getPhyParam(SCHEME_CONTROL, SchemeParam.PKTLEN);
            frameLenControl = frameLen;
         } else {
            frameLen = frameLenControl;
         }
      }
      frameLen = frameLen - otherOverheadBytes - myOverheadBytes;
      return frameLen;
   }

   private void calculationsInit() {
      // FIXME Rohit, Feb 8, 2012: remove source address from AT command request
      // MYADDR = getLinkParam(LinkParam.ADDR);

      int overheadlen = otherOverheadBytes + myOverheadBytes;
      if (overheadlen % 3 != 0) {
         overheadlen = overheadlen + (3 - overheadlen % 3);
      }
      // calculations for SCHEME_DATA
      overheadlen = overheadlen + 3;
      setPhyParam(SCHEME_DATA, SchemeParam.PKTLEN, overheadlen);

      int pkt_durationData = getPhyParam(SCHEME_DATA, SchemeParam.PKT_DURATION);
      // + phyProcTime + otherPRocTime;

      double alpha_d = (double) (pkt_durationData) / 1000.0; // in seconds
      long oldDurationData = 0;
      long newDurationData = 0;
      oldDurationData = getPhyParam(SCHEME_DATA, SchemeParam.PKT_DURATION);
      int maxpktlen = getPhyParam(SCHEME_DATA, SchemeParam.MAX_PKTLEN);
      if (maxpktlen % 3 != 0) {
         maxpktlen = maxpktlen - maxpktlen % 3;
      }
      log.fine("tsp:max pkt length:" + maxpktlen);
      // maxpktlen = 174;
      setPhyParam(SCHEME_DATA, SchemeParam.PKTLEN, maxpktlen);
      newDurationData = getPhyParam(SCHEME_DATA, SchemeParam.PKT_DURATION);
      log.fine("tsp:new duration:old duration:" + newDurationData + ":"
            + oldDurationData);

      int bytesGapData = maxpktlen - overheadlen;
      double beta_d = (double) (newDurationData - oldDurationData)
            / (double) (bytesGapData * 8 * 1000.0);
      log.fine("tsp:alpha_d:beta_d:" + alpha_d + ":" + beta_d);

      // calculations for SCHEME_CONTROL

      setPhyParam(SCHEME_CONTROL, SchemeParam.PKTLEN, overheadlen);
      int pkt_durationControl = getPhyParam(SCHEME_CONTROL,
            SchemeParam.PKT_DURATION);
      // + phyProcTime + otherPRocTime;

      double alpha_c = (double) (pkt_durationControl) / 1000.0; // in seconds
      long oldDurationControl = 0;
      long newDurationControl = 0;
      oldDurationControl = getPhyParam(SCHEME_CONTROL, SchemeParam.PKT_DURATION);
      // int maxpktlencontrol = getPhyParam(SCHEME_DATA,
      // SchemeParam.MAX_PKTLEN);
      setPhyParam(SCHEME_CONTROL, SchemeParam.PKTLEN, maxpktlen);
      newDurationControl = getPhyParam(SCHEME_CONTROL, SchemeParam.PKT_DURATION);

      int bytesGapControl = maxpktlen - overheadlen;
      double beta_c = (double) (newDurationControl - oldDurationControl)
            / (double) (bytesGapControl * 8 * 1000.0);
      log.fine("tsp:alpha_c:beta_c:" + alpha_c + ":" + beta_c);

      calc = new Calculator(alpha_d, alpha_c, beta_d, beta_c, this);

      int len = 0;
      int pktdur = 0;
      boolean sameLen;
      switch (TransportARQ.ARQ_TYPE) {
      case 0: // ARQ
      case 2: // variable block ARQ
         sameLen = true;
         len = (int) (Math.floor(calc.get_nd() / 8.0) + otherOverheadBytes + myOverheadBytes);
         // System.out.println("ARQ packet length:" + len);
         if (len % 3 != 0)
            len = len - (len % 3);
         // System.out.println("ARQ packet length:" + len);
         setPhyParam(SCHEME_DATA, SchemeParam.PKTLEN, len);

         pktdur = getPhyParam(SCHEME_DATA, SchemeParam.PKT_DURATION);
         while (sameLen) {
            if (pktdur == getPhyParam(SCHEME_DATA, SchemeParam.PKT_DURATION)) {
               len = len + 3;
            } else {
               sameLen = false;
               len = len - 3;
            }
            setPhyParam(SCHEME_DATA, SchemeParam.PKTLEN, len);
         }
         setPhyParam(SCHEME_DATA_TRIG, SchemeParam.PKTLEN, len);
         // setPhyParam(SCHEME_DATA_TRIG, SchemeParam.TX_TRIG, 4);
         // FIXME Rohit, Feb 8, 2012:implement the same for SCHEME_CONTROL
         int temp_blocksize = calc.get_m();
         // if (temp_blocksize > 48)
         // temp_blocksize = 48;
         blockSize = temp_blocksize;
         // blockSize = 1;
         blockSizeCalculated = blockSize;

         k = 0;
         jarqDelta = (int) (calc.getBest_delta() * 1000);
         // setPhyParam(SCHEME_CONTROL, SchemeParam.PKTLEN, 15);
         int controlpktlenarq = blockSize / 8 + 1 + otherOverheadBytes
               + myOverheadBytes;
         if (controlpktlenarq % 3 != 0)
            controlpktlenarq = controlpktlenarq + (3 - controlpktlenarq % 3);
         setPhyParam(SCHEME_CONTROL, SchemeParam.PKTLEN, controlpktlenarq);
         jarqPktdurControl = getPhyParam(SCHEME_CONTROL,
               SchemeParam.PKT_DURATION);

         break;
      case 1: // JARQ
      case 3:// rateless JARQ
         sameLen = true;
         len = (int) (Math.floor((double) calc.get_nd_jarq() / 8.0)
               + otherOverheadBytes + myOverheadBytes);
         if (len % 3 != 0)
            len = len - (len % 3);
         setPhyParam(SCHEME_DATA, SchemeParam.PKTLEN, len);

         pktdur = getPhyParam(SCHEME_DATA, SchemeParam.PKT_DURATION);
         // System.out.println("pkt length before:" + len);
         while (sameLen) {
            if (pktdur == getPhyParam(SCHEME_DATA, SchemeParam.PKT_DURATION)) {
               len = len + 3;
            } else {
               sameLen = false;
               len = len - 3;
            }
            setPhyParam(SCHEME_DATA, SchemeParam.PKTLEN, len);
         }
         setPhyParam(SCHEME_DATA, SchemeParam.PKTLEN, len);
         pktdur = getPhyParam(SCHEME_DATA, SchemeParam.PKT_DURATION);
         // System.out.println("pkt length after:" + len);
         setPhyParam(SCHEME_DATA_TRIG, SchemeParam.PKTLEN, len);
         setPhyParam(SCHEME_DATA_TRIG, SchemeParam.TX_TRIG, 4);
         // FIXME Rohit, Feb 8, 2012:implement the same for SCHEME_CONTROL
         int controlpktlen = calc.get_m_jarq() / 8 + 1 + otherOverheadBytes
               + myOverheadBytes;
         if (controlpktlen % 3 != 0)
            controlpktlen = controlpktlen + (3 - controlpktlen % 3);
         setPhyParam(SCHEME_CONTROL, SchemeParam.PKTLEN, controlpktlen);
         jarqPktDur = pktdur;
         jarqPktdurControl = getPhyParam(SCHEME_CONTROL,
               SchemeParam.PKT_DURATION);
         blockSize = calc.get_m_jarq();
         k = calc.get_k_jarq();
         double timeForAck = beta_c * len * 8 * 1000; // (ms)
         double delta = calc.getBest_delta() * 1000; // (ms)
         jarqDelta = (int) (Math.ceil(delta + timeForAck));
         break;

      default:
         break;
      }
      dataPacketDuration = getPhyParam(SCHEME_DATA, SchemeParam.PKT_DURATION) * 1000;
      int contduration = getPhyParam(SCHEME_CONTROL, SchemeParam.PKT_DURATION) * 1000;
      frameLenData = getPhyParam(SCHEME_DATA, SchemeParam.PKTLEN);
      frameLenControl = getPhyParam(SCHEME_CONTROL, SchemeParam.PKTLEN);
      System.out.println("data pktlen:" + frameLenData);
      System.out.println("control pktlen:" + frameLenControl);
      log.fine("data pktlen:" + frameLenData);
      log.fine("control pktlen:" + frameLenControl);
      log.fine("DATAPACKETDURATION:" + dataPacketDuration);
      log.fine("CONTROLPACKETDURATION:" + contduration);
      int distance = (int) (1500 * propDelay);
      log.fine("DISTANCELOGS:" + distance);
   }

   /**
    * method to calculate TX& RX proc time for maximum packet lengths
    * RX_PROCTIME = RX_PROCTIME for SCHEME_DATA TX_PROCTIME = TX_PROCTIME for
    * SCHEME_CONTROL
    */
   private void preLoopBack() {
      loopBackCount = 0;
      // set max possible packet length for data scheme
      int pktlen = setPossibleMaxPacketLength(SCHEME_DATA, MAXPKTDURATION);
      // set DSP loopback mode for modem
      setPhyParam(0, ModemParam.LOOPBACK, 1);
      // send testpkt for data scheme

      byte[] data = new byte[pktlen];
      for (int i = 0; i < pktlen; i++)
         data[i] = (byte) 11;
      FAPIMessage req = createMessage(FAPIMessage.REQUEST, phy, data);
      req.add("type", "PKT");
      req.add("length", pktlen);
      req.add("lowpri", 0);
      req.add("scheme", SCHEME_DATA);
      // send(req);
      FAPIMessage reply = request(req, 2000);
      if (reply != null && reply.getPerformative() == FAPIMessage.AGREE) {
         log.fine("1st loopback TX completed");
      } else {
         log.fine("1st loopback ERROR!!");
      }

      preLoopBack2();
   }

   private void preLoopBack2() {
      // set max possible packet length for control scheme
      int pktlenc = setPossibleMaxPacketLength(SCHEME_CONTROL, 350);
      // send testpkt for control scheme
      setPhyParam(0, ModemParam.LOOPBACK, 1);

      byte[] data1 = new byte[pktlenc];
      for (int i = 0; i < pktlenc; i++)
         data1[i] = (byte) 11;
      FAPIMessage req = createMessage(FAPIMessage.REQUEST, phy, data1);
      req.add("type", "PKT");
      req.add("length", pktlenc);
      req.add("lowpri", 0);
      req.add("scheme", SCHEME_CONTROL);
      // send(req);
      FAPIMessage reply = request(req, 2000);
      if (reply != null && reply.getPerformative() == FAPIMessage.AGREE) {
         log.fine("2nd loopback TX completed");
      } else {
         log.fine("2nd loopback ERROR!!");
      }
      postLoopBack();
   }

   private void postLoopBack() {

      // calculate the RX PROCTIME for DATA SCHEME
      rx_proctime_data = getPhyParam(SCHEME_DATA, SchemeParam.RX_PROC_TIME);
      tx_proctime_data = getPhyParam(SCHEME_DATA, SchemeParam.TX_PROC_TIME);
      // calculate the TX PROCTIME for CONTROL SCHEME
      tx_proctime_control = getPhyParam(SCHEME_CONTROL,
            SchemeParam.TX_PROC_TIME);
      rx_proctime_control = getPhyParam(SCHEME_CONTROL,
            SchemeParam.RX_PROC_TIME);

      // fill the VAR_PROCTIME
      // get the switching time for TX
      int switchingtime = getPhyParam(0, ModemParam.TX_DELAY);
      log.fine("tx proc data:" + tx_proctime_data);
      log.fine("rx proc data:" + rx_proctime_data);
      log.fine("tx proc control:" + tx_proctime_control);
      log.fine("rx proc control:" + rx_proctime_control);
      int total = tx_proctime_control + rx_proctime_data;
      log.fine("total:" + total);
      int divv = total / 1000;
      log.fine("divv" + divv);
      log.fine("switching:" + switchingtime);
      if (divv != 0)
         VAR_PROC_TIME = divv + switchingtime;
      else {
         FIXED_PROC_TIME = 0;
         VAR_PROC_TIME = 0;
      }
      // Time in ms
      log.fine("VAR_PROC_TIME=" + VAR_PROC_TIME);
      // THETA_TIME = VAR_PROC_TIME + GUARD_TIME + FIXED_PROC_TIME;
      THETA_TIME = GUARD_TIME + switchingtime;
      // reset the scheme pktlen to previous
      // setPhyParam(SCHEME_DATA, SchemeParam.PKTLEN, currDataPktlen);
      // setPhyParam(SCHEME_CONTROL, SchemeParam.PKTLEN, currControlPktlen);
      setPhyParam(0, ModemParam.LOOPBACK, 0); // reset the loopback

      calculationsInit();
   }

   private int setPossibleMaxPacketLength(int scheme, int maxduration) {
      int currpktlen = getPhyParam(scheme, SchemeParam.PKTLEN);
      int currpktduration = getPhyParam(scheme, SchemeParam.PKT_DURATION);
      int maxpktlen = getPhyParam(scheme, SchemeParam.MAX_PKTLEN);
      while (maxpktlen % 3 != 0)
         maxpktlen--;
      setPhyParam(scheme, SchemeParam.PKTLEN, maxpktlen);
      int maxpktduration = getPhyParam(scheme, SchemeParam.PKT_DURATION);

      int maxPossibleLength = (int) ((float) (maxpktlen - currpktlen)
            / (float) (maxpktduration - currpktduration) * maxduration);

      while (maxPossibleLength % 3 != 0) {
         maxPossibleLength--;
      }

      setPhyParam(scheme, SchemeParam.PKTLEN, maxPossibleLength);
      log.fine("Max possible length for scheme=" + scheme + " is ="
            + maxPossibleLength);
      return maxPossibleLength;

   }

   public String convertHexToString(String hex) {
      StringBuilder sb = new StringBuilder();
      StringBuilder temp = new StringBuilder();
      // 49204c6f7665204a617661 split into two characters 49, 20, 4c...
      // System.out.println("hex length:" + hex.length() + ":" + hex);
      for (int i = 0; i < hex.length() - 1; i += 2) {

         // grab the hex in pairs
         String output = hex.substring(i, (i + 2));

         // convert hex to decimal
         int decimal = Integer.parseInt(output, 16);
         // convert the decimal to character
         sb.append((char) decimal);
         // System.out.println(sb.toString());
         temp.append(decimal);

      }
      return sb.toString();
   }

   public String convertHexToString(String hex, int len) {
      StringBuilder sb = new StringBuilder();
      StringBuilder temp = new StringBuilder();
      // 49204c6f7665204a617661 split into two characters 49, 20, 4c...
      // System.out.println("hex length:" + hex.length() + ":" + hex);
      for (int i = 0; i < len; i += 2) {

         // grab the hex in pairs
         String output = hex.substring(i, (i + 2));

         // convert hex to decimal
         int decimal = Integer.parseInt(output, 16);
         // convert the decimal to character
         sb.append((char) decimal);
         // System.out.println(sb.toString());
         temp.append(decimal);

      }
      return sb.toString();
   }
}
